/************************************************************
 * Project Name		[Thinking_In_Java]
 * File Name     	[pkg_api_testing.Java_Math_BigDecimal.java]
 * Creation Date 	[14-Jun-2014]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 ************************************************************/
package pkg_api_testing;

import java.math.BigDecimal;

/**
 * java.math.BigDecimal
 * 
 * @author 不落的太阳(Sean Yang)
 * @version 1.0
 * @since JDK 6
 * 
 */
public class Java_Math_BigDecimal {

	/**
	 * Java提供了BigDecimal和BigInteger两个类来进行科学运算.
	 * 
	 * 只要能用int和float做的运算就可以使用BigInteger和BigDecimal类运算.
	 * 区别在于使用BigDecimal和BigInteger做运算必须采取方法调用.
	 * 
	 * BigInteger和BigDecimal都是不可变的, 在每一步运算的时候都会产生新的对象 由于创建对象会存在开销, 因此不适于大量的科学运算,
	 * 应该尽量采用基本类型来做科学运算和工程运算 BigInteger和BigDecimal一般用于商业运算.
	 * 
	 * 避免使用double构造BigDecimal, 因为某些数字根本无法用double精确表示.<br>
	 * 比如: new BigDecimal(0.1)得到的值并不是0.1, 而使用new BigDecimal("0.1")得到的结果是0.1.
	 * 因此使用BigDecimal进行运算, 尽量使用String进行构造.
	 * 
	 * 对于大数字类型的equals()方法认为0.1和0.1是相等的, 返回true; 但是认为0.1和0.10是不等的, 返回false<br>
	 * 对于大数字类型的compareTo()方法认为0.1和0.1相等, 0.1和0.10也相等
	 * 
	 * 出于对于某些小数运算仍然不能控制精度的考虑, BigDecimal依然要求程序员显式地控制舍入
	 */

	/**
	 * 默认除法运算的精度, 即保留小数点多少位
	 */
	public static final int DEFAULT_DIV_SCALE = 10;

	/**
	 * 提供精确的加法运算
	 * 
	 * @param d1
	 *            被加数
	 * @param d2
	 *            加数
	 * @return 两个参数的和
	 */
	public static double add(double d1, double d2) {
		BigDecimal decimal1 = new BigDecimal(Double.toString(d1));
		BigDecimal decimal2 = new BigDecimal(Double.toString(d2));
		return (decimal1.add(decimal2)).doubleValue();
	}

	/**
	 * 提供精确的减法运算
	 * 
	 * @param d1
	 *            被减数
	 * @param d2
	 *            减数
	 * @return 两个参数的差
	 */
	public static double subtract(double d1, double d2) {
		BigDecimal decimal1 = new BigDecimal(Double.toString(d1));
		BigDecimal decimal2 = new BigDecimal(Double.toString(d2));
		return (decimal1.subtract(decimal2)).doubleValue();
	}

	/**
	 * 提供精确的乘法
	 * 
	 * @param d1
	 *            被乘数
	 * @param d2
	 *            乘数
	 * @return 两个参数的积
	 */
	public static double mutiply(double d1, double d2) {
		BigDecimal decimal1 = new BigDecimal(Double.toString(d1));
		BigDecimal decimal2 = new BigDecimal(Double.toString(d2));
		return (decimal1.multiply(decimal2)).doubleValue();
	}

	/**
	 * 提供(相对)精确的除法运算, 调用同名的重载方法
	 * 
	 * @param d1
	 *            被除数
	 * @param d2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double divide(double d1, double d2) {
		return divide(d1, d2, DEFAULT_DIV_SCALE);
	}

	/**
	 * 提供(相对)精确的除法运算, 当除不尽时, 精确到10位, 以后的数字四舍五入
	 * 
	 * @param d1
	 *            被除数
	 * @param d2
	 *            除数
	 * @param scale
	 *            表示需要精确到小数点以后几位
	 * @return 两个参数的商
	 */
	private static double divide(double d1, double d2, int scale) {
		if (scale < 0) {
			System.err.println("除法的精度必须大于0");
			return 0;
		}
		BigDecimal decimal1 = new BigDecimal(Double.toString(d1));
		BigDecimal decimal2 = new BigDecimal(Double.toString(d2));
		return (decimal1.divide(decimal2, scale, BigDecimal.ROUND_HALF_UP))
				.doubleValue();
	}

	/**
	 * 测试方法
	 * 
	 * @param args
	 *            命令行参数
	 */
	public static void main(String[] args) {
		double d1 = 2.0;
		double d2 = 1.1;
		System.out.println(add(d1, d2));
		System.out.println(subtract(d1, d2));
		System.out.println(mutiply(d1, d2));
		System.out.println(divide(d1, d2));

		// System.out.println(0.1 == 0.10);
	}
}
